
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.Xml;

using Swaf;
using Swaf.Container;
using Swaf.DataAccess;

namespace Swaf.DataAccess
{
	/// <summary>
	/// Builds on top of the AdoNetDataRequest by executing its own main ADO.NET SQL and
	/// checking the Dar extensions manager to see if there are any features that extend 
	/// the feature this dar is from.  If there any extension Dars, its will execute them
	/// along with itself in the order they are found.</summary>
	/// <remarks>
	/// In order for DARs of this type to function properly, they MUST be located within
	/// a sub directory (or technology equivalent if not in a file system) within the parent
	/// catalog location.  Because the XmlCatalogContainerNode.NameInfo class provides
	/// a feature name derived from the first directory within the base directory of the
	/// catalog, the name of the directory the DAR catalog file is contained in matters.  
	/// Also, the Dar Extensions Manager must be present as a config partner or this 
	/// class will fail.
	/// 
	/// This class asks the DarExtensionsManager class for any features that extend dars
	/// from the feature this dar came from.  The DarExtensionsManager keeps a list of
	/// feature names that extend dars from other features.  The feature names used in 
	/// the configuration for marking that a feature extends dars from another feature
	/// need to match the name of the folder and the id used in the feature configuration
	/// file itself.  The following shows the places involved for the UserLogin feature
	/// of the framework gallery sample application:
	/// 
	/// The Feature configuration file for UserLogin has an id of UserLogin:
	/// <Feature id="UserLogin" name="User Login Information" >
	/// 
	/// The Feature configuration file for Gallery has an id of Gallery:
	/// <Feature id="Gallery" name="Photo Gallery Configuration">
	/// 
	/// The Gallery feature includes an dar extension configuration:
	/// <DarExtensions from="Gallery" for="UserLogin" />
	/// 
	/// The UserLogin dars are located in Catalogs\DataRequestDefinitions\UserLogin
	/// The Gallery dars are located in Catalogs\DataRequestDefinitions\Gallery
	/// 
	/// When bizrules in UserLogin call its dars, it will need to use the
	/// feature name:
	/// 
	/// Application.globalApp.appData.get("Data.UserLogin.GetUserInfo");
	/// 
	/// Also, it is up to any extention dars to use a bizobj formatter that will merge
	/// its data into the main bizobj list returned from this dar.  This dar will 
	/// place its results into the extraInfo as "_results0", so extension dars need
	/// to have their formatters written like the following example:
	/// 
	///   merge bizobj list Customer(Orders(Items(Product))) into _results0
	/// 
	/// 
	/// </remarks>
	public class MultiAdoNetDataRequest : IDataRequest, INameAddressSupport
	{
		protected XmlElement m_requestConfig = null;
		protected string m_myFeature;
		protected string m_myDarName;

		IList m_extendedDars = null;
		AdoNetDataRequest m_baseDar = null;

		/// <summary>
		/// Initializes this DAR based on the information provided.  This DAR requires a 
		/// XmlCatalogContainerNode.NameInfo to be passed as a parameter rather then just
		/// a simple string.  XmlCatalogContainerNode supports a constructor that takes 
		/// an XmlElement and either a string or a NameInfo.  Will also retrieve any 
		/// extension dars that exist and cache them into a member variable.</summary>
		/// <param name="req">The configuration information that defines the 
		/// instance.</param>
		/// <param name="catalogNameInfo">The NameInfo from the catalog that is used by
		/// this constructor to find its extensions.  Specifically, it uses the
		/// NameInfo.CatalogEntryName and NameInfo.EntryFeatureName in order to find
		/// them.</param>
		public MultiAdoNetDataRequest(XmlElement req, XmlCatalogContainerNode.NameInfo catalogNameInfo)
		{
			m_requestConfig = req;
			m_myDarName = catalogNameInfo.CatalogEntryName;
			m_myFeature = catalogNameInfo.EntryFeatureName;
			IList extFeatures = ExtensionsMgr.getExtensionFeatures(m_myFeature);

			XmlCatalogContainerNode darCatalog = Application.globalApp.appData.get("Data") as XmlCatalogContainerNode;
			Debug.Assert(darCatalog != null);

			IClassFactory factory = Application.globalApp.factories["DataRequestTypes"];
			Debug.Assert(factory != null);

			m_baseDar = new AdoNetDataRequest(req, catalogNameInfo.CatalogEntryName);
			m_baseDar.setDataResponseType(typeof(MultiAdoNetDataResponse));
			
			foreach (string featureName in extFeatures)
			{
				StringBuilder buff = new StringBuilder(255);
				buff.Append(featureName).Append(".Extensions.").Append(m_myFeature).Append(".").Append(m_myDarName);
				string darName = buff.ToString();
				object[] info = darCatalog.find(darName, false);
				if (info[0] != null)
				{
					//An extension DAR was found.
					IDataRequest extDar = info[0] as IDataRequest;
					if (extDar != null)
					{
						if (m_extendedDars == null)
							m_extendedDars = new ArrayList();
						m_extendedDars.Add(extDar);
					}
					else
						throw new DataAccessException(
							String.Format("Any Extension DAR used by the MultiAdoNetDataRequest most be a member of the AdoNetDataRequest family. Type {0} is not supported",
							extDar.GetType().Name));
				}
			}
		}

		#region IDataRequest Members

		/// <summary>
		/// Execute the DAR as configured in the XmlElement given during construction
		/// </summary>
		/// <param name="info">An IDictionary of parameters to pass into the dar and
		/// to recieve any out parameters from the dar.</param>
		/// <returns>The IDataResponse of the main query or null if the DAR has
		/// no results</returns>
		public IDataResponse execute(IDictionary info)
		{
			return execute((INameAddressSupport)new FlexiMap(info, true));
		}

		/// <summary>
		/// Will execute the base dar for this multidar dar.  If there is no response
		/// for this dar, then this method will execute all of the extension dars as
		/// well.</summary>
		/// <remarks>
		/// If the dar does as an IDataResonse, it will be of type MultiAdoNetDataResponse
		/// which will be given the list of extension dars.  The response
		/// object will execute the extension dars during its format processing.
		/// </remarks>
		/// <param name="info">An INameAddressSupport of parameters to pass into the
		/// dar and to recieve any out parameters from the dar.</param>
		/// <returns>The IDataResponse of the main query or null if the dar has
		/// no results.</returns>
		public IDataResponse execute(INameAddressSupport info)
		{
			IDataResponse rsp = m_baseDar.execute(info);
			MultiAdoNetDataResponse mrsp = rsp as MultiAdoNetDataResponse;
			if(mrsp != null)
				mrsp.ExtendedDars = m_extendedDars;
			if (rsp == null && m_extendedDars != null)
				foreach (IDataRequest dar in m_extendedDars)
					dar.execute(info);
			return rsp;
		}

		#endregion

		#region INameAddressSupport Members

		public object get(string name)
		{
			return get(name, null);
		}

		public object get(string name, object oneArg)
		{
			if (oneArg == null)
				return this;
			IDataResponse resp = null;

			if (oneArg is INameAddressSupport)
				resp = execute((INameAddressSupport)oneArg);
			else if (oneArg is IDictionary)
				resp = execute((IDictionary)oneArg);

			return (resp != null) ? resp.formatResults() : null;
		}

		public object get(string name, params object[] infoArgs)
		{
			if (infoArgs != null && infoArgs.Length != 0)
				return get(name, infoArgs[0]);
			return this;
		}

		public void put(string name, object newValue)
		{
			get(name, newValue);
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
			get(nameAddress, val);
		}

		public bool isGetOnly
		{
			get { return true; }
		}

		public XmlSerializationSupport SerializationSupport
		{
			get { return XmlSerializationSupport.AsElement; }
		}

		public string Xml
		{
			get
			{
				if (m_requestConfig != null)
					return m_requestConfig.OuterXml;
				else
					return "";
			}
		}

		public string getXml(string options)
		{
			return Xml;
		}

		public bool containsField(string nameAddress)
		{
			return false;
		}
		#endregion
		public IEnumerator GetEnumerator() { return null; }

		protected static DarExtensionsConfigPartner s_extensionsMgr = null;
		protected DarExtensionsConfigPartner ExtensionsMgr
		{
			get
			{
				if (s_extensionsMgr == null)
					lock (this)
						if (s_extensionsMgr == null)
							s_extensionsMgr = Application.globalApp.appData.
								get("sysObjs-DarExtensions") as DarExtensionsConfigPartner;
				Debug.Assert(s_extensionsMgr != null);
				return s_extensionsMgr;
			}
		}
	}
}
